/*
 * $Id$
 */

package corejs.rt;

import java.util.HashMap;
import java.util.Map;

/**
 *
 * @author pengyu
 */
public abstract class Value
   extends HashMap<String, Property> implements NameResolver<String, Value> {

   protected Value _proto;
   
   public Value() {this(null);}

   public Value(final Value proto) {
      _proto = proto;
   }
   
   public Value proto() {return _proto;}

   @Override
   public Value resolve(final String name) {
      final Property prop = get(name);
      return null != prop   ? prop.value()
         :   null != _proto ? _proto.resolve(name)
         :                    Undefined.UNDEFINED;
   }
   
   /**
    *
    * @param <T>
    * @param name
    * @param target
    * @return
    */
   public <T extends Value> T resolve(final String name, final Class<T> target) {
      final Value v = resolve(name);
      if (target.isInstance(v)) {
         return target.cast(v);
      }
      throw new TypeError();
   }

   public Value store(final String name, final int i) {
      return store(name, new Int(i));
   }

   public Value store(final String name, final String s) {
      return store(name, new Str(s));
   }

   /**
    * 
    * @param name
    * @param val
    * @return 
    */
   @Override
   public Value store(final String name, final Value val) {
      put(name, new Property(val));
      return val;
   }

   /**
    * 
    * @param props
    * @return 
    */
   public Value put(final Map<String, Value> props) {
      for (final Map.Entry<String, Value> e : props.entrySet()) {
         store(e.getKey(), e.getValue());
      }
      return this;
   }
   
   /**
    *
    * @return
    */
   public boolean isPrimitive() {
      return this instanceof Primitive;
   }
   
   public final boolean isNumber() {return this instanceof Number;}
   
   public final boolean isStr() {return this instanceof Str;}
   
   public final boolean isUndefined() {return this == Undefined.UNDEFINED;}
   
   public final boolean isNull() {return this == Null.NULL;}
   
   public final boolean isBool() {return this instanceof Bool;}
   
   public final boolean isNaN() {return this == Number.NaN;}
   
   /**
    *
    * @return
    */
   public abstract Bool toBool();
   
   /**
    *
    * @return
    */
   public Str typeof() {throw new TypeError();}
   
   /**
    *
    * @param <T>
    * @param clz
    * @return
    */
   public <T> T cast(final Class<T> clz) {
      if (clz.isInstance(this)) {
         return clz.cast(this);
      }
      throw new TypeError();
   }

   /**
    *
    * @param other
    * @return
    */
   public boolean equalsStrictly(final Value other) {
      return this == other;
   }

   /**
    *
    * @param y
    * @param ec
    * @return
    */
   public boolean equals(final Value y, final ExecutionContext ec) {
      return this == y || (
         (isPrimitive() || y.isPrimitive())
               && toPrimitive(this, ec).equals(toPrimitive(y, ec))
      );
   }

   /**
    *
    * @param src
    * @param ec
    * @return
    */
   public static Primitive toPrimitive(final Value src, final ExecutionContext ec) {
      return src.isPrimitive() ? src.cast(Primitive.class)
                               : src.cast(Obj.class).toPrimitive(ec);
   }

}
